package com.hww.common.util;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

public final class FileIOUtils {

    private static int sBufferSize = 8192;

    private FileIOUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 从输入流中读取数据并写入到指定文件。
     * 此方法用于将输入流中的数据逐块读取并写入到文件中。
     *
     * @param filePath 指定写入数据的目标文件路径。
     * @param is       提供要写入文件的数据的输入流。
     * @return 如果成功完成写入操作，则返回true；否则返回false。
     */
    public static boolean writeFileFromIS(final String filePath, final InputStream is) {
        return writeFileFromIS(getFileByPath(filePath), is, false);
    }

    /**
     * 从输入流中读取数据并写入到指定文件。
     * 此方法用于将输入流中的数据逐块读取并写入到文件中，支持追加模式。
     *
     * @param filePath 指定写入数据的文件路径。
     * @param is       提供要写入文件的数据的输入流。
     * @param append   指定是否以追加模式写入文件。
     * @return 如果成功完成写入操作，则返回true；否则返回false。
     */
    public static boolean writeFileFromIS(final String filePath, final InputStream is, final boolean append) {
        return writeFileFromIS(getFileByPath(filePath), is, append);
    }

    /**
     * 从输入流中读取数据并写入到指定文件。
     * 此方法用于将输入流中的数据逐块读取并写入到文件中。
     *
     * @param file 指定写入数据的目标文件。
     * @param is   提供要写入文件的数据的输入流。
     * @return 如果成功完成写入操作，则返回true；否则返回false。
     */
    public static boolean writeFileFromIS(final File file, final InputStream is) {
        return writeFileFromIS(file, is, false);
    }

    /**
     * 从输入流中读取数据并写入到指定文件。
     * 此方法用于将输入流中的数据逐块读取并写入到文件中，支持追加模式。
     *
     * @param file   指定写入数据的目标文件。
     * @param is     提供要写入文件的数据的输入流。
     * @param append 指定是否以追加模式写入文件。
     * @return 如果成功完成写入操作，则返回true；否则返回false。
     */
    public static boolean writeFileFromIS(final File file, final InputStream is, final boolean append) {
        if (!createOrExistsFile(file) || is == null)
            return false;
        OutputStream os = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(file, append));
            byte data[] = new byte[sBufferSize];
            int len;
            while ((len = is.read(data, 0, sBufferSize)) != -1) {
                os.write(data, 0, len);
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过字节流将字节数组写入文件。
     */
    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) {
        return writeFileFromBytesByStream(getFileByPath(filePath), bytes, false);
    }

    /**
     * 通过字节流将字节数组写入文件。
     */
    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append) {
        return writeFileFromBytesByStream(getFileByPath(filePath), bytes, append);
    }

    /**
     * 通过字节流将字节数组写入文件。
     */
    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) {
        return writeFileFromBytesByStream(file, bytes, false);
    }

    /**
     * 通过字节流将字节数组写入文件。
     * <p>
     * 此方法尝试将提供的字节数组写入指定文件。如果文件不存在，方法将尝试创建文件。
     * 写入操作可以在文件末尾追加数据，这取决于append参数的值。
     *
     * @param file   指定写入的文件，如果文件不存在，将尝试创建新文件。
     * @param bytes  要写入文件的字节数组。
     * @param append 如果为true，则在文件末尾追加数据；如果为false，则覆盖文件内容。
     * @return 如果写入操作成功，则返回true；如果出现错误，则返回false。
     */
    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append) {
        if (bytes == null || !createOrExistsFile(file))
            return false;
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(file, append));
            bos.write(bytes);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用文件通道的原因是它可以提供更高的性能，尤其是在处理大文件时。
     *
     * @param filePath 指定写入的文件路径，如果文件不存在，将尝试创建新文件。
     * @param bytes    要写入文件的字节数组。
     * @param isForce  如果为true，则强制将缓存的数据写入磁盘。
     * @return 如果成功写入文件，则返回true；否则返回false。
     */
    public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes,
                                                      final boolean isForce) {
        return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, false, isForce);
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用文件通道的原因是它可以提供更高的性能，尤其是在处理大文件时。
     *
     * @param filePath 指定写入的文件路径，如果文件不存在，将尝试创建新文件。
     * @param bytes    要写入文件的字节数组。
     * @param append   如果为true，则将字节追加到文件的末尾，否则将覆盖文件内容。
     * @param isForce  如果为true，则强制将缓存的数据写入磁盘。
     * @return 如果成功写入文件，则返回true；否则返回false。
     */
    public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean append,
                                                      final boolean isForce) {
        return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, append, isForce);
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用文件通道的原因是它可以提供更高的性能，尤其是在处理大文件时。
     *
     * @param file    指定写入的文件，如果文件不存在，将尝试创建新文件。
     * @param bytes   要写入文件的字节数组。
     * @param isForce 如果为true，则强制将缓存的数据写入磁盘。
     * @return 如果成功写入文件，则返回true；否则返回false。
     */
    public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean isForce) {
        return writeFileFromBytesByChannel(file, bytes, false, isForce);
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用文件通道的原因是它可以提供更高的性能，尤其是在处理大文件时。
     *
     * @param file    指定写入的文件，如果文件不存在，将尝试创建新文件。
     * @param bytes   要写入文件的字节数组。
     * @param append  如果为true，则将字节追加到文件的末尾，否则将覆盖文件内容。
     * @param isForce 如果为true，则强制将缓存的数据写入磁盘。
     * @return 如果成功写入文件，则返回true；否则返回false。
     */
    public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean append,
                                                      final boolean isForce) {
        if (bytes == null)
            return false;
        FileChannel fc = null;
        try {
            fc = new FileOutputStream(file, append).getChannel();
            fc.position(fc.size());
            fc.write(ByteBuffer.wrap(bytes));
            if (isForce)
                fc.force(true);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (fc != null) {
                    fc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用内存映射文件技术，这种方式在处理大文件时可能更有效率。
     *
     * @param filePath 指定写入的文件路径，如果文件不存在，将尝试创建新文件。
     * @param bytes    要写入文件的字节数组。
     * @param isForce  指定是否强制将修改写入磁盘。在某些情况下，修改可能不会立即写入磁盘，使用此参数可以确保立即写入。
     * @return 如果写入成功，返回true；否则，返回false。
     */
    public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean isForce) {
        return writeFileFromBytesByMap(filePath, bytes, false, isForce);
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用内存映射文件技术，这种方式在处理大文件时可能更有效率。
     *
     * @param filePath 指定写入的文件路径，如果文件不存在，将尝试创建新文件。
     * @param bytes    要写入文件的字节数组。
     * @param append   指定是否追加方式写入，如果为true，则在文件末尾追加内容；否则，覆盖原有内容。
     * @param isForce  指定是否强制将修改写入磁盘。在某些情况下，修改可能不会立即写入磁盘，使用此参数可以确保立即写入。
     * @return 如果写入成功，返回true；否则，返回false。
     */
    public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean append,
                                                  final boolean isForce) {
        return writeFileFromBytesByMap(getFileByPath(filePath), bytes, append, isForce);
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用内存映射文件技术，这种方式在处理大文件时可能更有效率。
     *
     * @param file    指定写入的文件，如果文件不存在，将尝试创建新文件。
     * @param bytes   要写入文件的字节数组。
     * @param isForce 指定是否强制将修改写入磁盘。在某些情况下，修改可能不会立即写入磁盘，使用此参数可以确保立即写入。
     * @return 如果写入成功，返回true；否则，返回false。
     */
    public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean isForce) {
        return writeFileFromBytesByMap(file, bytes, false, isForce);
    }

    /**
     * 通过文件通道将字节数组写入文件。
     * 使用内存映射文件技术，这种方式在处理大文件时可能更有效率。
     *
     * @param file    指定写入的文件，如果文件不存在，将尝试创建新文件。
     * @param bytes   要写入文件的字节数组。
     * @param append  指定是否追加方式写入，如果为true，则在文件末尾追加内容；否则，覆盖原有内容。
     * @param isForce 指定是否强制将修改写入磁盘。在某些情况下，修改可能不会立即写入磁盘，使用此参数可以确保立即写入。
     * @return 如果写入成功，返回true；否则，返回false。
     */
    public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean append,
                                                  final boolean isForce) {
        if (bytes == null || !createOrExistsFile(file))
            return false;
        FileChannel fc = null;
        try {
            fc = new FileOutputStream(file, append).getChannel();
            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, fc.size(), bytes.length);
            mbb.put(bytes);
            if (isForce)
                mbb.force();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (fc != null) {
                    fc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将字符串写入到指定文件中。(适合处理文本文件)
     *
     * @param filePath 要写入的文件对象路径，如果文件不存在，将尝试创建文件。
     * @param content  要写入的字符串内容。
     * @return 如果写入操作成功，则返回true；如果出现错误，则返回false。
     */
    public static boolean writeFileFromString(final String filePath, final String content) {
        return writeFileFromString(getFileByPath(filePath), content, false);
    }

    /**
     * 将字符串写入到指定文件中。(适合处理文本文件)
     *
     * @param filePath 要写入的文件对象路径，如果文件不存在，将尝试创建文件。
     * @param content  要写入的字符串内容。
     * @param append   指定是否追加写入，如果为true，则内容将被追加到文件末尾；如果为false，则会覆盖原有内容。
     * @return 如果写入操作成功，则返回true；如果出现错误，则返回false。
     */
    public static boolean writeFileFromString(final String filePath, final String content, final boolean append) {
        return writeFileFromString(getFileByPath(filePath), content, append);
    }

    /**
     * 将字符串写入到指定文件中。(适合处理文本文件)
     *
     * @param file    要写入的文件对象，如果文件不存在，将尝试创建文件。
     * @param content 要写入的字符串内容。
     * @return 如果写入操作成功，则返回true；如果出现错误，则返回false。
     */
    public static boolean writeFileFromString(final File file, final String content) {
        return writeFileFromString(file, content, false);
    }

    /**
     * 将字符串写入到指定文件中。(适合处理文本文件)
     *
     * @param file    要写入的文件对象，如果文件不存在，将尝试创建文件。
     * @param content 要写入的字符串内容。
     * @param append  指定是否追加写入，如果为true，则内容将被追加到文件末尾；如果为false，则会覆盖原有内容。
     * @return 如果写入操作成功，则返回true；如果出现错误，则返回false。
     */
    public static boolean writeFileFromString(final File file, final String content, final boolean append) {
        if (file == null || content == null)
            return false;
        if (!createOrExistsFile(file))
            return false;
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(file, append));
            bw.write(content);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // the divide line of write and read
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Return the lines in file.
     *
     * @param filePath The path of file.
     * @return the lines in file
     */
    public static List<String> readFile2List(final String filePath) {
        return readFile2List(getFileByPath(filePath), null);
    }

    /**
     * Return the lines in file.
     *
     * @param filePath    The path of file.
     * @param charsetName The name of charset.
     * @return the lines in file
     */
    public static List<String> readFile2List(final String filePath, final String charsetName) {
        return readFile2List(getFileByPath(filePath), charsetName);
    }

    /**
     * 从文件中读取指定行范围的内容到列表中。
     *
     * @param file 要读取的文件，不能为null。
     * @return 包含文件指定行范围内容的字符串列表，如果文件不存在或无法读取，则返回空列表。
     */
    public static List<String> readFile2List(final File file) {
        return readFile2List(file, 0, 0x7FFFFFFF, null);
    }

    /**
     * 从文件中读取指定行范围的内容到列表中。
     *
     * @param file        要读取的文件，不能为null。
     * @param charsetName 文件的字符集名称，例如："UTF-8"，不能为null。
     * @return 包含文件指定行范围内容的字符串列表，如果文件不存在或无法读取，则返回空列表。
     */
    public static List<String> readFile2List(final File file, final String charsetName) {
        /*指定从第一行开始读取到文件末尾*/
        return readFile2List(file, 0, 0x7FFFFFFF, charsetName);
    }

    /**
     * 从文件中读取指定行范围的内容到列表中。
     *
     * @param filePath 要读取的文件路径，必须存在。
     * @param st       起始行号（包含）。
     * @param end      结束行号（包含）。
     * @return 包含指定行范围内容的字符串列表，如果文件不存在或行范围不合法，则返回null。
     */
    public static List<String> readFile2List(final String filePath, final int st, final int end) {
        return readFile2List(getFileByPath(filePath), st, end, null);
    }

    /**
     * 从文件中读取指定行范围的内容到列表中。
     *
     * @param filePath    要读取的文件路径，必须存在。
     * @param st          起始行号（包含）。
     * @param end         结束行号（包含）。
     * @param charsetName 文件的字符集名称，如果为空或只包含空格，则使用默认字符集。
     * @return 包含指定行范围内容的字符串列表，如果文件不存在或行范围不合法，则返回null。
     */
    public static List<String> readFile2List(final String filePath, final int st, final int end,
                                             final String charsetName) {
        return readFile2List(getFileByPath(filePath), st, end, charsetName);
    }

    /**
     * 从文件中读取指定行范围的内容到列表中。
     *
     * @param file 要读取的文件，必须存在。
     * @param st   起始行号（包含）。
     * @param end  结束行号（包含）。
     * @return 包含指定行范围内容的字符串列表，如果文件不存在或行范围不合法，则返回null。
     */
    public static List<String> readFile2List(final File file, final int st, final int end) {
        return readFile2List(file, st, end, null);
    }

    /**
     * 从文件中读取指定行范围的内容到列表中。
     *
     * @param file        要读取的文件，必须存在。
     * @param st          起始行号（包含）。
     * @param end         结束行号（包含）。
     * @param charsetName 文件的字符集名称，如果为空或只包含空格，则使用默认字符集。
     * @return 包含指定行范围内容的字符串列表，如果文件不存在或行范围不合法，则返回null。
     */
    public static List<String> readFile2List(final File file, final int st, final int end, final String charsetName) {
        if (!isFileExists(file))
            return null;
        if (st > end)
            return null;
        BufferedReader reader = null;
        try {
            String line;
            int curLine = 1;
            List<String> list = new ArrayList<>();
            if (StringUtils.isSpace(charsetName)) {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            } else {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName));
            }
            while ((line = reader.readLine()) != null) {
                if (curLine > end)
                    break;
                if (st <= curLine && curLine <= end)
                    list.add(line);
                ++curLine;
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从文件中读取内容并转换为字符串。
     *
     * @param filePath 要读取的文件路径，不能为null。
     * @return 文件的内容作为字符串。如果文件不存在或无法读取，则返回null。
     */
    public static String readFile2String(final String filePath) {
        return readFile2String(getFileByPath(filePath), null);
    }

    /**
     * 从文件中读取内容并转换为字符串。
     *
     * @param filePath    要读取的文件路径，不能为null。
     * @param charsetName 字符集名称，用于将字节序列解码为字符串。如果为null或空字符串，则使用平台的默认字符集。
     * @return 文件的内容作为字符串。如果文件不存在或无法读取，则返回null。如果指定的字符集不受支持，则会打印异常堆栈跟踪并返回空字符串。
     */
    public static String readFile2String(final String filePath, final String charsetName) {
        return readFile2String(getFileByPath(filePath), charsetName);
    }

    /**
     * 从文件中读取内容并转换为字符串。
     *
     * @param file 要读取的文件，不能为null。
     * @return 文件的内容作为字符串。如果文件不存在或无法读取，则返回null
     */
    public static String readFile2String(final File file) {
        return readFile2String(file, null);
    }

    /**
     * 从文件中读取内容并转换为字符串。
     *
     * @param file        要读取的文件，不能为null。
     * @param charsetName 字符集名称，用于将字节序列解码为字符串。如果为null或空字符串，则使用平台的默认字符集。
     * @return 文件的内容作为字符串。如果文件不存在或无法读取，则返回null。如果指定的字符集不受支持，则会打印异常堆栈跟踪并返回空字符串。
     */
    public static String readFile2String(final File file, final String charsetName) {
        byte[] bytes = readFile2BytesByStream(file);
        if (bytes == null)
            return null;
        if (StringUtils.isSpace(charsetName)) {
            return new String(bytes);
        } else {
            try {
                return new String(bytes, charsetName);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return "";
            }
        }
    }

    /**
     * 从文件中读取全部字节内容。
     * 该方法通过文件输入流读取文件内容，并将其转换为字节数组。
     * 如果文件不存在，方法将返回null。
     *
     * @param filePath 需要读取的文件对象的路径。
     * @return 文件的字节数组表示，如果文件不存在或读取失败则返回null。
     */
    public static byte[] readFile2BytesByStream(final String filePath) {
        return readFile2BytesByStream(getFileByPath(filePath));
    }

    /**
     * 从文件中读取全部字节内容。
     * 该方法通过文件输入流读取文件内容，并将其转换为字节数组。
     * 如果文件不存在，方法将返回null。
     *
     * @param file 需要读取的文件对象。
     * @return 文件的字节数组表示，如果文件不存在或读取失败则返回null。
     */
    public static byte[] readFile2BytesByStream(final File file) {
        if (!isFileExists(file))
            return null;
        try {
            return ConvertUtils.inputStream2Bytes(new FileInputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过文件通道读取文件内容到字节数组。
     * 使用文件通道完成文件的读取，相比于传统的文件读取方式，文件通道可以更有效地处理大文件。
     *
     * @param filePath 需要读取的文件对象的路径。
     * @return 文件的字节数组，如果文件不存在或读取过程中发生异常，则返回null。
     */
    public static byte[] readFile2BytesByChannel(final String filePath) {
        return readFile2BytesByChannel(getFileByPath(filePath));
    }

    /**
     * 通过文件通道读取文件内容到字节数组。
     * 使用文件通道完成文件的读取，相比于传统的文件读取方式，文件通道可以更有效地处理大文件。
     *
     * @param file 需要读取的文件对象。
     * @return 文件的字节数组，如果文件不存在或读取过程中发生异常，则返回null。
     */
    public static byte[] readFile2BytesByChannel(final File file) {
        if (!isFileExists(file))
            return null;
        FileChannel fc = null;
        try {
            fc = new RandomAccessFile(file, "r").getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate((int) fc.size());
            while (true) {
                if (!((fc.read(byteBuffer)) > 0))
                    break;
            }
            return byteBuffer.array();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fc != null) {
                    fc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Return the bytes in file by map.
     *
     * @param filePath The path of file.
     * @return the bytes in file
     */
    public static byte[] readFile2BytesByMap(final String filePath) {
        return readFile2BytesByMap(getFileByPath(filePath));
    }

    /**
     * 通过MappedByteBuffer读取文件内容到字节数组。
     * Return the bytes in file by map.
     *
     * @param file The file.
     * @return the bytes in file
     */
    public static byte[] readFile2BytesByMap(final File file) {
        if (!isFileExists(file))
            return null;
        FileChannel fc = null;
        try {
            fc = new RandomAccessFile(file, "r").getChannel();
            int size = (int) fc.size();
            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
            byte[] result = new byte[size];
            mbb.get(result, 0, size);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fc != null) {
                    fc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Set the buffer's size.
     * <p>
     * Default size equals 8192 bytes.
     * </p>
     *
     * @param bufferSize The buffer's size.
     */
    public static void setBufferSize(final int bufferSize) {
        sBufferSize = bufferSize;
    }

    /**
     * 根据文件路径获取文件对象。
     * <p>
     * 此方法通过检查文件路径是否为空白来决定是否返回null或一个新的File对象。如果文件路径是空白的，方法返回null，
     * 否则返回一个基于提供的文件路径创建的File对象。
     *
     * @param filePath 文件的路径。可以是相对路径或绝对路径。
     * @return 如果文件路径不是空白，则返回对应的File对象；如果是空白，则返回null。
     */
    public static File getFileByPath(final String filePath) {
        return StringUtils.isSpace(filePath) ? null : new File(filePath);
    }

    /**
     * 创建或检查文件是否存在。
     *
     * @param filePath 指定文件对象的路径
     * @return 如果文件存在（新创建或已存在）且是一个文件，则返回true；否则返回false。
     */
    public static boolean createOrExistsFile(final String filePath) {
        return createOrExistsFile(getFileByPath(filePath));
    }

    /**
     * 创建或检查文件是否存在。
     * 如果文件不存在，尝试创建它。如果文件存在，确认它是一个文件而不是目录。
     *
     * @param file 指定的文件对象
     * @return 如果文件存在（新创建或已存在）且是一个文件，则返回true；否则返回false。
     */
    private static boolean createOrExistsFile(final File file) {
        if (file == null)
            return false;
        if (file.exists()) {
            /*文件存在，确保它是一个文件而不是文件夹*/
            return file.isFile();
        }
        /*检查父目录是否存在*/
        if (!createOrExistsDir(file.getParentFile()))
            return false;
        try {
            /*创建新文件*/
            return file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建或检查一个目录是否存在。如果该目录不存在，则尝试创建它。
     *
     * @param file 指定的文件对象，代表要检查或创建的目录。
     * @return 如果文件对象不为空，并且（该目录已存在且是一个目录，或者成功创建了该目录），则返回true；否则返回false。
     */
    private static boolean createOrExistsDir(final File file) {
        return file != null && ((file.exists() && file.isDirectory()) ? (file.isDirectory()) : file.mkdirs());
    }

    /**
     * 创建或检查一个目录是否存在。如果该目录不存在，则尝试创建它。
     */
    public static boolean createOrExistsDir(final String filePath) {
        return createOrExistsDir(getFileByPath(filePath));
    }

    /**
     * 判断文件是否存在
     *
     * @param file 指定文件对象
     * @return 如果文件存在，则返回true；否则返回false。
     */
    private static boolean isFileExists(final File file) {
        return file != null && file.exists();
    }


    /**
     * 将对象写入到指定路径的文件中。
     * (ObjectInputStream主要用于将之前序列化的对象恢复为内存中的Java对象)
     * 该方法会首先检查文件是否存在，如果不存在则尝试创建文件。
     * 然后，使用ObjectOutputStream将对象序列化并写入文件。
     * 如果在操作过程中发生IO异常，将会打印异常堆栈跟踪。
     *
     * @param object 需要写入文件的对象，必须实现Serializable接口。
     * @param path   指定的文件路径，包括文件名和扩展名。
     */
    public static void writeObject(Object object, String path) {
        File fileByPath = getFileByPath(path);
        boolean orExistsFile = createOrExistsFile(fileByPath);
        if (!orExistsFile) {
            return;
        }
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(fileByPath));
            oos.writeObject(object);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                    oos = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 从指定路径读取并反序列化一个对象。配合{@code #writeObject(Object, String)}使用
     *
     * @param path 文件路径。
     * @return 反序列化后的对象，如果文件不存在或读取失败则返回null。
     */
    public static Object readObject(String path) {
        File fileByPath = getFileByPath(path);
        boolean orExistsFile = createOrExistsFile(fileByPath);
        if (!orExistsFile) {
            return null;
        }
        ObjectInputStream ois = null;
        Object object = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(fileByPath));
            object = ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                try {
                    ois.close();
                    ois = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return object;
        }
    }
}
